/*
	Copyright (c) 2012 by karmaleaving@qq.com. All rights RESERVED.
*/

#ifndef __THREAD_H_VERSION_KL__
#define __THREAD_H_VERSION_KL__

#include "config.h"
#ifdef _KL_UNIX_
#error not surport this version
#else
#include <windows.h>
#endif // _KL_UNIX_

KL_BEGIN
class ThreadJob
{
public:
	virtual bool run ()
	{	// Thread will call like "while(run());"
		// override this function to finish your job.
		return false;
	}
	virtual ~ThreadJob()
	{
	}
};
class Mutex
{
public:
#ifdef _KL_UNIX_
#else
	typedef CRITICAL_SECTION mutex_type;
	static void _initialize(mutex_type &mutex)
	{
		::InitializeCriticalSection(&mutex);
	}
	static void _destroy(mutex_type &mutex)
	{
		::DeleteCriticalSection(&mutex);
	}
	static void _lock(mutex_type &mutex)
	{
		::EnterCriticalSection(&mutex);
	}	
	static bool _try_lock(mutex_type &mutex)
	{
		return ::TryEnterCriticalSection(&mutex) == TRUE ? true : false;
	}
	static void _unlock(mutex_type &mutex)
	{
		::LeaveCriticalSection(&mutex);
	}
#endif // _KL_UNIX_
protected:
	mutex_type _mutex;
	Mutex(Mutex const & o)
		: _mutex(o._mutex)
	{}
	void operator = (Mutex const & o)
	{
		_mutex = o._mutex;
	}
public:
	Mutex()
	{
		_initialize(_mutex);
	}
	void lock()
	{
		_lock(_mutex);
	}
	void try_lock()
	{
		_try_lock(_mutex);
	}
	void unlock()
	{
		_unlock(_mutex);
	}
	~Mutex()
	{
		_destroy(_mutex);
	}
};
class MutexLocker
{
private:
	Mutex * pmtx;
	MutexLocker(MutexLocker const &);
	void operator = (MutexLocker const &);
public:
	MutexLocker(Mutex * pmtx)
		: pmtx(pmtx)
	{
		if(pmtx) pmtx->lock();
	}
	~MutexLocker()
	{
		if(pmtx) pmtx->unlock();
	}
};
class ConditionVariable
	{	
	public:
#ifdef _KL_UNIX_
#else
		typedef CONDITION_VARIABLE cond_type;
		typedef CRITICAL_SECTION mutex_type;
		static void _initialize(cond_type &cond, mutex_type &mutex)
		{
			::InitializeConditionVariable(&cond);
			::InitializeCriticalSection(&mutex);
		}
		static void _sleep(cond_type &cond, mutex_type mutex)
		{
			::SleepConditionVariableCS(&cond, &mutex, ~DWORD(0));
		}
		static void _sleep(cond_type &cond, mutex_type mutex, u4_t ms)
		{
			::SleepConditionVariableCS(&cond, &mutex, (DWORD)ms);
		}
		static void _awake(cond_type &cond, mutex_type &)
		{
			::WakeConditionVariable(&cond);
		}
		static void _awake_all(cond_type &cond, mutex_type &)
		{
			::WakeAllConditionVariable(&cond);
		}
		static void _destroy(cond_type &, mutex_type &mutex)
		{
			::DeleteCriticalSection(&mutex);
		}
#endif // _KL_UNIX_
	protected:
		cond_type _cond;
		mutex_type _mutex;
	public:
		ConditionVariable()
		{
			_initialize(_cond, _mutex);
		}
		void sleep()
		{
			_sleep(_cond, _mutex);
		}
		void sleep(u4_t ms)
		{
			_sleep(_cond, _mutex, ms);
		}
		void awake()
		{
			_awake(_cond, _mutex);
		}
		void awake_all()
		{
			_awake_all(_cond, _mutex);
		}
		~ConditionVariable()
		{
			_destroy(_cond, _mutex);
		}
	};
template<typename JobType = ThreadJob>
class Thread
{
public:
	typedef HANDLE thread_type;
	typedef DWORD id_type;
	typedef DWORD exit_type;
	static const LONG_PTR InvalidThread = (LONG_PTR)INVALID_HANDLE_VALUE;
	static const exit_type NormalExit = 0;
	static const u4_t SleepTime = 4000; // 4 second.
#ifdef _KL_UNIX_
#else
	static thread_type Create(LPTHREAD_START_ROUTINE start, void *parameter)
	{
		return ::CreateThread(LPSECURITY_ATTRIBUTES(0),
			SIZE_T(0), start, parameter, DWORD(0), LPDWORD(0));
	}
	static thread_type Get_handle()
	{
		return ::GetCurrentThread();
	}
	static id_type Get_id(thread_type handle)
	{
		return ::GetThreadId(handle);
	}
	static id_type Get_id()
	{
		return ::GetCurrentThreadId();
	}
	static void Sleep(u4_t ms)
	{
		::Sleep(DWORD(ms));
	}
	static void Terminate(thread_type handle, exit_type const &value = 0)
	{
		::TerminateThread(handle, value);
	}
#endif
protected:
	JobType _job;
	ConditionVariable		_outer;
	ConditionVariable		_inner;
	bool _asleep, _alive;
	thread_type _handle;
public:
	enum State
	{
		Running, Sleeping, Stoping, Stoped
	};
	State state() const
	{
		return _alive ? (_asleep ? Sleeping : Running)
			: (_asleep ? Stoped : Stoping);
	}
	thread_type handle() const
	{
		return _handle;
	}
	id_type id() const
	{
		return Get_id(_handle);
	}

public:
	Thread()
		: _job()
		, _alive(true)
		, _asleep(true)
	{
		_handle = Create((LPTHREAD_START_ROUTINE)&_thread_fun, this);
		if(_handle == (thread_type)InvalidThread)
			_alive = false;
	}
	template<typename JobType>
	Thread(JobType const &job)
		: _job(job)
		, _alive(true)
		, _asleep(false)
	{
		_handle = Create((LPTHREAD_START_ROUTINE)&_thread_fun, this);
		if(_handle == (thread_type)InvalidThread)
			_alive = false, _asleep = true;
	}
	~Thread()
	{
	}
	void stop_wait()
	{
		_alive = false;
		_inner.awake();
		while(!_asleep) _outer.sleep(SleepTime);
	}
	bool wait()
	{	// if the state is Running, wait for switching to Sleeping.
		// if Stoping, wait for switching to Stoped.
		while(!_asleep) _outer.sleep(SleepTime);
	}
	void stop()
	{
		_alive = false;
		_inner.awake();
	}
	void terminate()
	{
		Terminate(_handle);
	}
	template<typename JobType>
	bool awake(JobType const &job)
	{
		if(!_alive) return false;
		while(!_asleep) _outer.sleep(SleepTime);
		if(_alive && _asleep)
		{
			_job = job;
			_asleep = false;
			_inner.awake( );
			return true;
		}
		return false;
	}
	bool awake()
	{
		if(!_alive) return false;
		while(!_asleep) _outer.sleep(SleepTime);
		if(_alive && _asleep)
		{
			_asleep = false;
			_inner.awake();
			return true;
		}
		return false;
	}
private:
	static exit_type _thread_fun(Thread *Thread)
	{
		while(Thread->_sleep())
		{
			while(Thread->_alive && Thread->_job.run());
			Thread->_asleep = true;
			Thread->_outer.awake();
		}
		return NormalExit;
	}
	bool _sleep()
	{
		while(_asleep && _alive)
			_inner.sleep(SleepTime);
		if(!_alive) return false;
		return true;
	}
};
KL_END
#endif // __THREAD_H_VERSION_KL__
